﻿#include <QtMath>
#include "JZGLCamera.h"
#include "JZGLView.h"


JZGLCamera::JZGLCamera() 
    :m_fovY(45.0f)
{
    m_widget = nullptr;
    lookAt(QVector3D(0.0f, 0.0f, 10.0f), QVector3D(0.0f, 0.0f, 0.0f), QVector3D(0.0f, 1.0f, 0.0f));
    m_near = 0.01f;
    m_far = 100.0f;
    m_width = 320;
    m_height = 240;
}

void JZGLCamera::setWidget(JZGLView* widget)
{
    m_widget = widget;
}

JZGLView* JZGLCamera::widget()
{
    return m_widget;
}

QVector3D JZGLCamera::position()
{
    return m_eye;
}

QVector3D JZGLCamera::lookPosition()
{
    return m_center;
}

void JZGLCamera::update()
{
    if (m_widget)
        m_widget->update();
}

void JZGLCamera::lookAt(QVector3D pos, QVector3D target, QVector3D up)
{
    m_eye = pos;
    m_center = target;
    m_up = up;
    update();
}

void JZGLCamera::setFovY(float fovY)
{
    m_fovY = fovY;
    update();
}

void JZGLCamera::setViewport(int width, int height)
{
    m_width = width;
    m_height = height;
    update();
}

float JZGLCamera::farPlane()
{
    return m_far;
}

void JZGLCamera::setFarPlane(float far_plane)
{
    m_far = far_plane;
    m_widget->update();
}

float JZGLCamera::nearPlane()
{
    return m_near;
}

void JZGLCamera::setNearPlane(float near_plane)
{
    m_near = near_plane;
    m_widget->update();
}

QMatrix4x4 JZGLCamera::getViewMatrix() 
{
    QMatrix4x4 ret;
    ret.lookAt(m_eye, m_center, getWorldUp());
    return ret;
}

QMatrix4x4 JZGLCamera::getProjectionMatrix() 
{
    QMatrix4x4 ret;
    ret.perspective(m_fovY, (float)m_width / (float)m_height, m_near, m_far);    
    return ret;
}

QMatrix4x4 JZGLCamera::getWindowMatrix()
{
    QMatrix4x4 viewportMatrix;
    // 根据视口矩阵的构建规则设置矩阵元素
    viewportMatrix(0, 0) = m_width / 2.0;
    viewportMatrix(0, 3) = m_width / 2.0;
    viewportMatrix(1, 1) = -m_height / 2.0;
    viewportMatrix(1, 3) = m_height / 2.0;
    viewportMatrix(2, 2) = 1.0;
    viewportMatrix(2, 3) = 0.0;
    viewportMatrix(3, 3) = 1.0;
    return viewportMatrix;
}

QPoint JZGLCamera::project(float x, float y, float z)
{
    auto view = getViewMatrix();
    auto projection = getProjectionMatrix();
    QMatrix4x4 mvp = projection * view;

    // 从裁剪坐标转换为归一化设备坐标 (NDC)
    QVector3D ndcPoint = mvp.map(QVector3D(x,y,z));

    // 从 NDC 转换为屏幕坐标
    int px = (ndcPoint.x() + 1.0f) * m_width / 2.0f;
    int py = (1.0f - ndcPoint.y()) * m_height / 2.0f;

    return QPoint(px, py);
}

void JZGLCamera::setRotate(float x, float y, float z)
{
    update();
}

void JZGLCamera::rotate(float& x, float& y, float& z)
{
    update();
}

QVector3D JZGLCamera::unProject(int x, int y, float depth)
{
    int w = m_width;
    int h = m_height;

    y = h - y;
    float ndcX = (2.0f * static_cast<float>(x) / static_cast<float>(w)) - 1.0f;
    float ndcY = (2.0f * static_cast<float>(y) / static_cast<float>(h)) - 1.0f;
    float ndcZ = 2.0 * depth - 1.0;

    auto view = getViewMatrix();
    auto projection = getProjectionMatrix();

    QVector4D clip_ray = QVector4D(ndcX, ndcY, ndcZ, 1.0f);
    QVector4D eye_ray = projection.inverted() * clip_ray;
    QVector4D world_ray = view.inverted() * eye_ray;
    //转笛卡尔坐标
    if (world_ray.w() != 0.0f) {
        world_ray /= world_ray.w();
    }

    return world_ray.toVector3D();
}

QVector3D JZGLCamera::getWorldUp() 
{
    float dis = m_eye.distanceToPoint(m_center);
    QVector3D wrold_center = m_center - m_eye;
    QVector3D local_center = QVector3D(0,0,-dis);
    QQuaternion rotationUp = QQuaternion::rotationTo(local_center, wrold_center);

    // 应用总旋转到本地 up 向量得到世界 up 向量
    QVector3D worldUpResult = rotationUp.rotatedVector(m_up);
    return worldUpResult;
}

void JZGLCamera::lookAtRotate(RoateType type, float angle)  //绕观察点旋转
{
    // 将角度转换为弧度
    double radAngle = qDegreesToRadians(angle);

    // 计算相机相对于观察点的位置
    QVector3D relativePos = m_eye - m_center;

    QQuaternion rotationQuaternion;
    if (type == Yaw || type == Pitch) 
    {
        if (type == Yaw) {
            // 偏航旋转（绕 Y 轴）
            rotationQuaternion = QQuaternion::fromAxisAndAngle(QVector3D(0, 1, 0), angle);
        }
        else {
            // 俯仰旋转（绕 X 轴）
            rotationQuaternion = QQuaternion::fromAxisAndAngle(QVector3D(1, 0, 0), angle);            
        }

        // 应用旋转到相对位置
        relativePos = rotationQuaternion.rotatedVector(relativePos);

        // 更新相机位置
        m_eye = m_center + relativePos;
    }
    else
    {
        // 翻滚旋转（绕视线方向）
        QVector3D viewDir = (m_center - m_eye).normalized();
        rotationQuaternion = QQuaternion::fromAxisAndAngle(viewDir, angle);
        
        // 应用旋转到 up 向量
        m_up = rotationQuaternion.rotatedVector(m_up).normalized();
    }

    update();
}

// 根据欧拉角计算 lookAt 和 up
void JZGLCamera::rotate(float yaw, float pitch, float roll) {
    // 将角度转换为弧度
    float yawRad = qDegreesToRadians(yaw);
    float pitchRad = qDegreesToRadians(pitch);
    float rollRad = qDegreesToRadians(roll);

    // 初始的 lookAt 向量（指向负 Z 轴）
    QVector3D initialLookAt = (m_center - m_eye);
    // 初始的 up 向量（指向正 Y 轴）
    QVector3D initialUp = m_up;

    // 创建绕 Y 轴（偏航）的四元数
    QQuaternion yawQuaternion = QQuaternion::fromAxisAndAngle(QVector3D(0, 1, 0), yaw);
    // 创建绕 X 轴（俯仰）的四元数
    QQuaternion pitchQuaternion = QQuaternion::fromAxisAndAngle(QVector3D(1, 0, 0), pitch);
    // 创建绕 Z 轴（翻滚）的四元数
    QQuaternion rollQuaternion = QQuaternion::fromAxisAndAngle(QVector3D(0, 0, 1), roll);

    // 组合四元数（先偏航，再俯仰，最后翻滚）
    QQuaternion combinedQuaternion = rollQuaternion * pitchQuaternion * yawQuaternion;

    // 应用组合四元数到初始 lookAt 向量
    QVector3D lookAt = combinedQuaternion.rotatedVector(initialLookAt);
    // 应用组合四元数到初始 up 向量
    m_up = combinedQuaternion.rotatedVector(initialUp);

    // 归一化 lookAt 和 up 向量
    m_up.normalize();
    m_center = m_eye + lookAt;
    update();
}